package dq.study.java.grammar.generator;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author dongqun
 * @date 2020/3/6 16:50
 */
public class Main {
    /**
     * 上界通配符 <? extend T> 只能读取不能写入
     * 下界通配符 <? super T> 只能写入不能读取
     *
     * @param args
     */
    public static void main(String[] args) {
        Plate<? extends Fruit> plate = new Plate<>(new Apple());

//        plate.set(new Apple());//error
        Fruit fruit = plate.get();
        Object mySelfObject = plate.get();
//        Apple apple = plate.get();//error
//        plate.set(new Object());
//        plate.set(new Fruit());

        //原因是编译器只知道容器内是Fruit或者它的派生类，但具体是什么类型不知道。
        // 可能是Fruit？可能是Apple？也可能是Banana，RedApple，GreenApple？
        // 编译器在看到后面用Plate赋值以后，盘子里没有被标上有“苹果”。
        // 而是标上一个占位符：CAP#1，来表示捕获一个Fruit或Fruit的子类，具体是什么类不知道，代号CAP#1。
        // 然后无论是想往里插入Apple或者Meat或者Fruit编译器都不知道能不能和这个CAP#1匹配，所以就都不允许

        //所以通配符<?>和类型参数的区别就在于，对编译器来说所有的T都代表同一种类型。
        // 比如下面这个泛型方法里，三个T都指代同一个类型，要么都是String，要么都是Integer。
        // public <T> List<T> fill(T... t); 但通配符<?>没有这种约束


        Plate<? super Fruit> plate1 = new Plate<Fruit>(new Fruit());
//        Apple apple1 = (Apple) plate1.get();//error
        Object o = plate1.get();
        Fruit fruit1 = (Fruit) plate1.get();
        plate1.set(new Apple());


        //PECS（producer extend customer super）
        //频繁往外读取内容的，适合用上界Extends
        //经常往里插入的，适合用下界Super


        Plate<? super Apple> plate2 = new Plate<>();
//        plate2.set(new Fruit());//error


        Plate<? extends Fruit> plate3 = new Plate<>();
//        plate3.set(new Fruit());//error


        Plate<? extends Fruit> plate4 = new Plate<>(new Apple());
        Fruit fruit2 = plate4.get();
        Apple apple = (Apple) plate4.get();

        // ? extends 上界通配符只用来接受数据
        // 1.Java泛型是编译器做的限制，运行时会泛型擦除
        // 2.泛型是指具体类型，加了？的上界代表可以接受这一类，但你不能存，这是为了确保类型安全，相反，如果可以set，请使用具体类型，又何必加？呢
        // 3.泛型下界正好相反，你可以set他和他的子类，因为超类是确定的
        List<? extends Fruit> fruits = new ArrayList<>();
//        fruits.add(new Fruit());//error
//        fruits.add(new Apple());//error



        List<? super Apple> apples = new ArrayList<>();
        apples.add(new Apple());
//        apples.add(new Fruit());//error
        Fruit fruit3 = (Fruit) apples.get(0);
        System.out.println(fruit3);

        // 泛型类型擦除： https://blog.csdn.net/briblue/article/details/76736356

    }
}
